package com.kexio.file.service.impl;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;

import javax.imageio.ImageIO;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.kexio.file.domain.FileInfo;
import com.kexio.file.dto.PreviewResult;
import com.kexio.file.service.FilePreviewService;
import com.kexio.file.service.FileService;

import net.coobird.thumbnailator.Thumbnails;

/**
 * 文件预览服务实现类
 * 
 * @author kexio
 */
@Service
public class FilePreviewServiceImpl implements FilePreviewService {

    private static final Logger log = LoggerFactory.getLogger(FilePreviewServiceImpl.class);

    @Autowired
    private FileService fileService;

    // 支持的图片格式
    private static final List<String> IMAGE_EXTENSIONS = Arrays.asList(
        "jpg", "jpeg", "png", "gif", "bmp", "webp", "svg"
    );

    // 支持的文档格式
    private static final List<String> DOCUMENT_EXTENSIONS = Arrays.asList(
        "pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx"
    );

    // 支持的文本格式
    private static final List<String> TEXT_EXTENSIONS = Arrays.asList(
        "txt", "md", "log", "xml", "json", "csv", "java", "js", "html", "css"
    );

    // 支持的视频格式
    private static final List<String> VIDEO_EXTENSIONS = Arrays.asList(
        "mp4", "avi", "mov", "wmv", "flv", "webm"
    );

    // 支持的音频格式
    private static final List<String> AUDIO_EXTENSIONS = Arrays.asList(
        "mp3", "wav", "flac", "aac", "ogg"
    );

    @Override
    public boolean isPreviewable(FileInfo fileInfo) {
        String extension = fileInfo.getFileExtension();
        if (extension == null) {
            return false;
        }
        
        extension = extension.toLowerCase();
        return IMAGE_EXTENSIONS.contains(extension) ||
               DOCUMENT_EXTENSIONS.contains(extension) ||
               TEXT_EXTENSIONS.contains(extension) ||
               VIDEO_EXTENSIONS.contains(extension) ||
               AUDIO_EXTENSIONS.contains(extension);
    }

    @Override
    public PreviewResult generatePreview(FileInfo fileInfo, InputStream fileStream) {
        String extension = fileInfo.getFileExtension();
        if (extension == null) {
            return PreviewResult.unsupported("无法识别文件类型");
        }

        extension = extension.toLowerCase();

        try {
            if (IMAGE_EXTENSIONS.contains(extension)) {
                return generateImagePreview(fileInfo, fileStream);
            } else if (DOCUMENT_EXTENSIONS.contains(extension)) {
                return generateDocumentPreview(fileInfo, fileStream);
            } else if (TEXT_EXTENSIONS.contains(extension)) {
                return generateTextPreview(fileInfo, fileStream);
            } else if (VIDEO_EXTENSIONS.contains(extension)) {
                return generateVideoPreview(fileInfo, fileStream);
            } else if (AUDIO_EXTENSIONS.contains(extension)) {
                return generateAudioPreview(fileInfo, fileStream);
            } else {
                return PreviewResult.unsupported("不支持该文件类型的预览");
            }
        } catch (Exception e) {
            log.error("生成文件预览失败: fileId={}, filename={}", 
                     fileInfo.getId(), fileInfo.getOriginalFilename(), e);
            return PreviewResult.unsupported("预览生成失败: " + e.getMessage());
        }
    }

    @Override
    public String getPreviewUrl(Long fileId, Long userId) {
        // 检查权限
        if (!fileService.checkFilePermission(fileId, userId, "read")) {
            throw new RuntimeException("无文件预览权限");
        }
        
        return "/api/files/preview/" + fileId + "?userId=" + userId;
    }

    @Override
    public byte[] generateThumbnail(FileInfo fileInfo, InputStream fileStream, int width, int height) {
        String extension = fileInfo.getFileExtension();
        if (extension == null) {
            return null;
        }

        extension = extension.toLowerCase();

        try {
            if (IMAGE_EXTENSIONS.contains(extension)) {
                return generateImageThumbnail(fileStream, width, height);
            } else if ("pdf".equals(extension)) {
                return generatePdfThumbnail(fileStream, width, height);
            } else {
                return null;
            }
        } catch (Exception e) {
            log.error("生成缩略图失败: fileId={}, filename={}", 
                     fileInfo.getId(), fileInfo.getOriginalFilename(), e);
            return null;
        }
    }

    @Override
    public int getDocumentPageCount(FileInfo fileInfo, InputStream fileStream) {
        String extension = fileInfo.getFileExtension();
        if (!"pdf".equals(extension)) {
            return 1;
        }

        try (PDDocument document = PDDocument.load(fileStream)) {
            return document.getNumberOfPages();
        } catch (Exception e) {
            log.error("获取PDF页数失败: fileId={}", fileInfo.getId(), e);
            return 1;
        }
    }

    @Override
    public byte[] generatePagePreview(FileInfo fileInfo, InputStream fileStream, int pageNumber) {
        String extension = fileInfo.getFileExtension();
        if (!"pdf".equals(extension)) {
            return null;
        }

        try (PDDocument document = PDDocument.load(fileStream)) {
            if (pageNumber < 1 || pageNumber > document.getNumberOfPages()) {
                throw new IllegalArgumentException("页码超出范围: " + pageNumber);
            }

            PDFRenderer renderer = new PDFRenderer(document);
            BufferedImage image = renderer.renderImageWithDPI(pageNumber - 1, 150); // 150 DPI

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImageIO.write(image, "PNG", baos);
            return baos.toByteArray();

        } catch (Exception e) {
            log.error("生成PDF页面预览失败: fileId={}, pageNumber={}", 
                     fileInfo.getId(), pageNumber, e);
            return null;
        }
    }

    // ==================== 私有方法 ====================

    private PreviewResult generateImagePreview(FileInfo fileInfo, InputStream fileStream) {
        // 图片直接返回访问URL
        String previewUrl = "/api/files/download/" + fileInfo.getId();
        return PreviewResult.image(previewUrl);
    }

    private PreviewResult generateDocumentPreview(FileInfo fileInfo, InputStream fileStream) throws IOException {
        String extension = fileInfo.getFileExtension().toLowerCase();
        
        switch (extension) {
            case "pdf":
                return generatePdfPreview(fileInfo, fileStream);
            case "doc":
            case "docx":
                return generateWordPreview(fileInfo, fileStream);
            case "xls":
            case "xlsx":
                return generateExcelPreview(fileInfo, fileStream);
            case "ppt":
            case "pptx":
                return generatePowerPointPreview(fileInfo, fileStream);
            default:
                return PreviewResult.unsupported("不支持该文档格式的预览");
        }
    }

    private PreviewResult generatePdfPreview(FileInfo fileInfo, InputStream fileStream) throws IOException {
        try (PDDocument document = PDDocument.load(fileStream)) {
            int pageCount = document.getNumberOfPages();
            String previewUrl = "/api/files/preview/pdf/" + fileInfo.getId();
            return PreviewResult.pdf(previewUrl, pageCount);
        }
    }

    private PreviewResult generateWordPreview(FileInfo fileInfo, InputStream fileStream) throws IOException {
        try (XWPFDocument document = new XWPFDocument(fileStream)) {
            StringBuilder content = new StringBuilder();
            content.append("<div class='document-preview'>");
            
            // 提取段落文本
            document.getParagraphs().forEach(paragraph -> {
                String text = paragraph.getText();
                if (text != null && !text.trim().isEmpty()) {
                    content.append("<p>").append(escapeHtml(text)).append("</p>");
                }
            });
            
            content.append("</div>");
            
            return PreviewResult.html(content.toString());
        }
    }

    private PreviewResult generateExcelPreview(FileInfo fileInfo, InputStream fileStream) throws IOException {
        try (XSSFWorkbook workbook = new XSSFWorkbook(fileStream)) {
            StringBuilder content = new StringBuilder();
            content.append("<div class='excel-preview'>");
            
            // 遍历工作表
            workbook.forEach(sheet -> {
                content.append("<h3>").append(escapeHtml(sheet.getSheetName())).append("</h3>");
                content.append("<table border='1' cellpadding='5'>");
                
                // 只显示前20行
                int maxRows = Math.min(sheet.getLastRowNum() + 1, 20);
                for (int i = 0; i < maxRows; i++) {
                    var row = sheet.getRow(i);
                    if (row != null) {
                        content.append("<tr>");
                        for (int j = 0; j < row.getLastCellNum(); j++) {
                            var cell = row.getCell(j);
                            String cellValue = cell != null ? cell.toString() : "";
                            content.append("<td>").append(escapeHtml(cellValue)).append("</td>");
                        }
                        content.append("</tr>");
                    }
                }
                content.append("</table><br/>");
            });
            
            content.append("</div>");
            return PreviewResult.html(content.toString());
        }
    }

    private PreviewResult generatePowerPointPreview(FileInfo fileInfo, InputStream fileStream) throws IOException {
        try (XMLSlideShow ppt = new XMLSlideShow(fileStream)) {
            StringBuilder content = new StringBuilder();
            content.append("<div class='powerpoint-preview'>");
            
            var slides = ppt.getSlides();
            for (int i = 0; i < slides.size(); i++) {
                content.append("<div class='slide'>");
                content.append("<h3>幻灯片 ").append(i + 1).append("</h3>");
                
                var slide = slides.get(i);
                slide.getShapes().forEach(shape -> {
                    // 提取文本内容
                    try {
                        String text = shape.getShapeName();
                        if (text != null && !text.trim().isEmpty()) {
                            content.append("<p>").append(escapeHtml(text)).append("</p>");
                        }
                    } catch (Exception e) {
                        // 忽略无法提取文本的形状
                    }
                });
                
                content.append("</div><hr/>");
            }
            
            content.append("</div>");
            return PreviewResult.html(content.toString());
        }
    }

    private PreviewResult generateTextPreview(FileInfo fileInfo, InputStream fileStream) throws IOException {
        StringBuilder content = new StringBuilder();
        try (InputStreamReader reader = new InputStreamReader(fileStream, StandardCharsets.UTF_8)) {
            char[] buffer = new char[8192];
            int totalRead = 0;
            int maxSize = 100 * 1024; // 最多读取100KB
            
            int read;
            while ((read = reader.read(buffer)) != -1 && totalRead < maxSize) {
                content.append(buffer, 0, Math.min(read, maxSize - totalRead));
                totalRead += read;
            }
            
            if (totalRead >= maxSize) {
                content.append("\n\n... [文件过大，仅显示前100KB内容]");
            }
        }
        
        return PreviewResult.text(content.toString());
    }

    private PreviewResult generateVideoPreview(FileInfo fileInfo, InputStream fileStream) {
        // 视频预览返回HTML5视频播放器
        String videoUrl = "/api/files/download/" + fileInfo.getId();
        StringBuilder html = new StringBuilder();
        html.append("<div class='video-preview'>");
        html.append("<video controls width='100%' height='400'>");
        html.append("<source src='").append(videoUrl).append("' type='").append(fileInfo.getContentType()).append("'>");
        html.append("您的浏览器不支持视频播放。");
        html.append("</video>");
        html.append("</div>");
        
        return PreviewResult.html(html.toString());
    }

    private PreviewResult generateAudioPreview(FileInfo fileInfo, InputStream fileStream) {
        // 音频预览返回HTML5音频播放器
        String audioUrl = "/api/files/download/" + fileInfo.getId();
        StringBuilder html = new StringBuilder();
        html.append("<div class='audio-preview'>");
        html.append("<audio controls>");
        html.append("<source src='").append(audioUrl).append("' type='").append(fileInfo.getContentType()).append("'>");
        html.append("您的浏览器不支持音频播放。");
        html.append("</audio>");
        html.append("</div>");
        
        return PreviewResult.html(html.toString());
    }

    private byte[] generateImageThumbnail(InputStream fileStream, int width, int height) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Thumbnails.of(fileStream)
            .size(width, height)
            .keepAspectRatio(true)
            .outputFormat("PNG")
            .toOutputStream(baos);
        return baos.toByteArray();
    }

    private byte[] generatePdfThumbnail(InputStream fileStream, int width, int height) throws IOException {
        try (PDDocument document = PDDocument.load(fileStream)) {
            if (document.getNumberOfPages() == 0) {
                return null;
            }
            
            PDFRenderer renderer = new PDFRenderer(document);
            BufferedImage image = renderer.renderImageWithDPI(0, 150); // 第一页，150 DPI
            
            // 缩放到指定尺寸
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            Thumbnails.of(image)
                .size(width, height)
                .keepAspectRatio(true)
                .outputFormat("PNG")
                .toOutputStream(baos);
            
            return baos.toByteArray();
        }
    }

    private String escapeHtml(String text) {
        return text.replace("&", "&amp;")
                   .replace("<", "&lt;")
                   .replace(">", "&gt;")
                   .replace("\"", "&quot;")
                   .replace("'", "&#39;");
    }
}
